
/*-----------------------------------------------------------------------------\
|                          ChipCFlow Block2Code V2.0                           |
|                                                                              |
| Desenvolvedor: Felipe Simões Lage Gomes Duarte                               |
| Orientador: Prof. Dr. Jorge Luiz e Silva                                     |
|                                                                              |
| Main Project: ChipCflow                                                      |
| Período: Agosto de 2010 a Julho de 2011                                      |
| Codigo: Main.java                                                            |
| Versão da IDE: NetBeans IDE 6.9.1 (Build 201011082200)                       |
| Java: 1.6.0_21; Java HotSpot(TM) Client VM 17.0-b17                          |
| Sistema Operacional: Windows 7 versão 6.1 executando em x86; pt_BR (nb)      |
|                                                                              |
|             O presente trabalho foi realizado com apoio do CNPq              |
|   Conselho Nacional de Desenvolvimento Científico  e Tecnológico - Brasil    |
\-----------------------------------------------------------------------------*/
package controler;

import Tradutor.Traducao;
import java.io.*;
import view.*;
import data.*;
import components.*;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.Iterator;

public class Action {

  private static Action SINGLETON;
  private static Integer currentIndex;
  private static ArrayList<Dados> data;
  private static ArrayList<Op_GenericoData> op_generico;
  private Components componentAux;
  private Operadores opAux; //auxiliar para Operadores
  private Barramento conectAux; //auxiliar para Barramento
  private Texto textAux;
  private int lx, ly; //last coord. - necessario para o funcionamento do mover

  private Action() {
    currentIndex = -1;
    data = new ArrayList<Dados>();
    op_generico = new ArrayList<Op_GenericoData>();
  }
  
  public static Action getInstance(){
    if(SINGLETON == null)
      SINGLETON = new Action();
    return SINGLETON;
  }

  public static ArrayList<Op_GenericoData> getOp_generico() {
    return op_generico;
  }
  
  

  public static void newDados() {
    data.add(new Dados());
    currentIndex++;
  }
  
  public static void addComponents(Components comp){
    data.get(currentIndex).addComponents(comp);
  }

  public int getCurrentIndex() {
    return currentIndex;
  }

  public void setCurrentIndex(int currentIndex) {
    Action.currentIndex = currentIndex;
  }

  public int getDataSize() {
    return data.size();
  }

  public static void removeDataAt(int tabNumber) {
    data.remove(tabNumber);
  }
  
  public void newOp_Generico(Op_GenericoData op_genericodata){
    op_generico.add(op_genericodata);
    Block2Code.getInstance().popupMenuOperadores();
  }

  /*
   * Components
   */
  public static void paintComponents(Graphics g) {
    /*desenha todos os Components*/
    Iterator<Components> it = data.get(currentIndex).getComponents();
    while (it.hasNext()) {
      it.next().desenha((Graphics2D) g);
    }
  }

  public void remover(int x, int y) {
    Iterator<Components> it = data.get(currentIndex).getComponents();
    while (it.hasNext()) {
      if (it.next().procuraForma(x, y)) {
        it.remove();
      }
    }
  }

  public void find(int x, int y) {
    this.componentAux = null;
    Iterator<Components> it = data.get(currentIndex).getComponents();
    while (it.hasNext()) {
      this.componentAux = it.next();//recupera o component do vetor
      //verifica se o component se encontra na coordenada x,y
      if (!componentAux.procuraForma(x, y)) {
        //caso nao encontre, flush no componentAux
        this.componentAux = null;
      } else {//caso encontre a forma, retorna da funçao com o componentAux apontando
        //para o component encontrado
        lx = x;
        ly = y;//inicializa as ultimas coordenadas
        return;
      }
    }
  }

  public void mover(int x, int y) {
    if (this.componentAux != null) {
      int Dx = 0, Dy = 0;
      Dx = x - lx;
      Dy = y - ly;
      this.componentAux.mover(Dx, Dy);
      lx = x;
      ly = y;
    }
  }

  /*
   *
   * Texto
   *
   */
  public void escrever(int x, int y) {
    textAux = new Texto();//novo texto
    textAux.setMx(x);//set a coordenada x do texto
    textAux.setMy(y);//set a coordenada y do texto
    if (textAux.setTextoadd()) {
      textAux.settings(Fonte.getInstance().getFonte(),
              Fonte.getInstance().getEstilo(),
              Fonte.getInstance().getTamanho(),
              Fonte.getInstance().getCor());
      data.get(currentIndex).addComponents(textAux);
    }
  }

  /*
   *
   * Operadores
   *
   */
  public void setOpAux(Operadores opaux) {
    this.opAux = opaux;
  }

  public void criarOperador(int x, int y) {
    opAux.setMx(x);
    opAux.setMy(y);
    opAux.updateBarName();
    data.get(currentIndex).addComponents(opAux);
  }

  public void setOpAuxCoordenadas(int x, int y) {
    opAux.setMx(x);
    opAux.setMy(y);
  }

  public void finalizarOperador(int x, int y) {
    //conectar operador
    conectarOperador(opAux);
    //instaciar novo operador auxiliar
    if (opAux instanceof Op_Branch) {
      opAux = new Op_Branch();
    } else if (opAux instanceof Op_Copy) {
      opAux = new Op_Copy();
    } else if (opAux instanceof Op_DMerge) {
      opAux = new Op_DMerge();
    } else if (opAux instanceof Op_Decider) {
      opAux = new Op_Decider();
    } else if (opAux instanceof Op_NDMerge) {
      opAux = new Op_NDMerge();
    } else if (opAux instanceof Op_Operator) {
      opAux = new Op_Operator();
    } else if (opAux instanceof Op_In) {
      opAux = new Op_In();
    } else if (opAux instanceof Op_Out) {
      opAux = new Op_Out();
    }
  }

  private void conectarOperador(Operadores op) {
    Components _comp = null;
    int P = Opcoes.getInstance().getPrecisao();//pega estaticamente a precisão
    Iterator<Components> it = data.get(currentIndex).getComponents();

    //para todos elementos do vetor de components
    while (it.hasNext()) {
      _comp = it.next();//recupera o component do vetor
      if (_comp instanceof Barramento) { //se for Barramento
        Barramento _bar = (Barramento) _comp;
        Iterator<Conector> _it = op.getBar();
        //para todos os conectores do operador
        while (_it.hasNext()) {
          Conector _con = _it.next();
          if ((Math.abs(_bar.getCiX() - _con.getX()) < P) && (Math.abs(_bar.getCiY() - _con.getY()) < P) && (!_bar.getCi().isConected()) && (!_con.isConected())) {
            _con.conect(it.hashCode()); //conectar operador
            _bar.conectCi(_con.getX(), _con.getY(), data.get(currentIndex).componentsSize() - 1); //conectar barramento
            break;
          } else if ((Math.abs(_bar.getCfX() - _con.getX()) < P) && (Math.abs(_bar.getCfY() - _con.getY()) < P) && (!_bar.getCf().isConected()) && (!_con.isConected())) {
            _con.conect(it.hashCode()); //conectar operador
            _bar.conectCf(_con.getX(), _con.getY(), data.get(currentIndex).componentsSize() - 1); //conectar barramento
            break;
          }
        }
      }
    }
  }

  /*
   *
   * Barramento
   *
   */
  public void criarConector(int x, int y) {
    conectAux = new Barramento(x, y);
    conectAux.updateName();
    //adiciona o conector ao vetor de conectores
    data.get(currentIndex).addComponents(conectAux);
    conectarBarramento(conectAux);
  }

  public void setConectAuxEnd(int x, int y) {
    conectAux.setEnd(x, y);
  }

  public void conectAuxAdd(int x, int y) {
    conectAux.add(x, y);
  }

  public void finalizarConector(int x, int y) {
    conectAux.setEnd(x, y);
    conectarBarramento(conectAux);
  }

  private void conectarBarramento(Barramento conectAux) {
    Components _comp = null;
    int P = Opcoes.getInstance().getPrecisao();//pega estaticamente a precisão
    Iterator<Components> it = data.get(currentIndex).getComponents();

    //para todos elementos do vetor de components
    while (it.hasNext()) {
      _comp = it.next();//recupera o component do vetor
      if (_comp == conectAux) {
        break;
      }
      //conectar barramento em um barramento
      if (_comp instanceof Barramento) { //se for Barramento
        Barramento _bar = (Barramento) _comp;
        //Ci - Ci
        if ((Math.abs(conectAux.getCiX() - _bar.getCiX()) < P) && (Math.abs(conectAux.getCiY() - _bar.getCiY()) < P)
                && (!conectAux.getCi().isConected()) && (!_bar.getCi().isConected())) {
          _bar.conectCi(_bar.getCiX(), _bar.getCiY(), data.get(currentIndex).componentsSize() - 1); //conectar operador
          conectAux.conectCi(_bar.getCiX(), _bar.getCiY(), it.hashCode()); //conectar barramento
          continue;
        }
        //Ci - Cf
        if ((Math.abs(conectAux.getCiX() - _bar.getCfX()) < P) && (Math.abs(conectAux.getCiY() - _bar.getCfY()) < P)
                && (!conectAux.getCi().isConected()) && (!_bar.getCf().isConected())) {
          _bar.conectCf(_bar.getCfX(), _bar.getCfY(), data.get(currentIndex).componentsSize() - 1); //conectar operador
          conectAux.conectCi(_bar.getCfX(), _bar.getCfY(), it.hashCode()); //conectar barramento
          continue;
        }
        //Cf - Ci
        if ((Math.abs(conectAux.getCfX() - _bar.getCiX()) < P) && (Math.abs(conectAux.getCfY() - _bar.getCiY()) < P)
                && (!conectAux.getCf().isConected()) && (!_bar.getCi().isConected())) {
          _bar.conectCi(_bar.getCiX(), _bar.getCiY(), data.get(currentIndex).componentsSize() - 1); //conectar operador
          conectAux.conectCf(_bar.getCiX(), _bar.getCiY(), it.hashCode()); //conectar barramento
          continue;
        }
        //Cf - Cf
        if ((Math.abs(conectAux.getCfX() - _bar.getCfX()) < P) && (Math.abs(conectAux.getCfY() - _bar.getCfY()) < P)
                && (!conectAux.getCf().isConected()) && (!_bar.getCf().isConected())) {
          _bar.conectCf(_bar.getCfX(), _bar.getCfY(), data.get(currentIndex).componentsSize() - 1); //conectar operador
          conectAux.conectCf(_bar.getCfX(), _bar.getCfY(), it.hashCode()); //conectar barramento
          continue;
        }
      } //conectar barramento em um operador
      else if ((_comp instanceof Op_Branch) || (_comp instanceof Op_Copy) || (_comp instanceof Op_DMerge)
              || (_comp instanceof Op_Decider) || (_comp instanceof Op_Generico) || (_comp instanceof Op_In)
              || (_comp instanceof Op_NDMerge) || (_comp instanceof Op_Operator) || (_comp instanceof Op_Out)) {
        Operadores _ops = (Operadores) _comp;
        //para todos elementos do vetor de components
        Iterator<Conector> _it = _ops.getBar();
        //para todos os conectores do operador
        while (_it.hasNext()) {
          Conector _con = _it.next();
          if ((Math.abs(conectAux.getCiX() - _con.getX()) < P) && (Math.abs(conectAux.getCiY() - _con.getY()) < P) && (!conectAux.getCi().isConected()) && (!_con.isConected())) {
            _con.conect(it.hashCode()); //conectar operador
            conectAux.conectCi(_con.getX(), _con.getY(), _it.hashCode()); //conectar barramento
            break;
          } else if ((Math.abs(conectAux.getCfX() - _con.getX()) < P) && (Math.abs(conectAux.getCfY() - _con.getY()) < P) && (!conectAux.getCf().isConected()) && (!_con.isConected())) {
            _con.conect(it.hashCode()); //conectar operador
            conectAux.conectCf(_con.getX(), _con.getY(), _it.hashCode()); //conectar barramento
            break;
          }
        }
      }
    }
  }

  /*
   *
   */
  public void save(File fp) throws Exception {
    ObjectOutputStream obj_out = null;
    try {//Tenta salvar os bytes no arquivo
      obj_out = new ObjectOutputStream(new FileOutputStream(fp));
      obj_out.writeObject(data);
    } catch (Exception e) {
      e.printStackTrace();
      throw new Exception("Error ao salvar o projeto\n" + e.getMessage());
    } finally {
      if (obj_out != null) {
        obj_out.close();
      }
    }
  }

  public void load(File fp) throws Exception {
    ObjectInputStream obj_in = null;
    try {
      obj_in = new ObjectInputStream(new FileInputStream(fp));
      data = (ArrayList<Dados>) obj_in.readObject();//Le os bytes do arquivo  
    } catch (Exception e) {
      e.printStackTrace();
      throw new Exception("Error ao Carregar o projeto\n" + e.getMessage());
    } finally {
      if (obj_in != null) {
        obj_in.close();
      }
    }
  }
  
  /*
   *
   */
  public void traduzir() throws Exception {
      new Traducao().start();      
  }
  
}
